﻿using System;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using DynamicInterop;

namespace Wings.NativeInterop
{
    using heatmap_t = IntPtr;
    using heatmap_stamp_t = IntPtr;

    public static class HeatMap
    {
        const string kDllName = "heatmap.dll";
        static UnmanagedDll _native = null;
        static bool _loaded = false;

        static HeatMap()
        {
            try
            {
                _native = new UnmanagedDll(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, kDllName));
                _loaded = true;
            }
            catch (Exception ex)
            {

            }
        }

        static void InvokeFunction<TDelegate>(params object[] args) where TDelegate : Delegate
        {
            if (_loaded)
                _native.GetFunction<TDelegate>(typeof(TDelegate).Name).DynamicInvoke(args);
        }

        static TResult InvokeFunction<TResult, TDelegate>(params object[] args) where TDelegate : Delegate
        {
            if (_loaded)
                return (TResult)_native.GetFunction<TDelegate>(typeof(TDelegate).Name).DynamicInvoke(args);

            return default(TResult);
        }

        //包装函数定义
        public static heatmap_t new_heatmap(uint w, uint h) => InvokeFunction<heatmap_t, heatmap_new>(w, h);

        public static void free_heatmap(heatmap_t h) => InvokeFunction<heatmap_free>(h);

        public static void add_point(heatmap_t h, uint x, uint y) => InvokeFunction<heatmap_add_point>(h, x, y);
        public static void add_point_with_stamp(heatmap_t h, uint x, uint y, heatmap_stamp_t stamp) 
            => InvokeFunction<heatmap_add_point_with_stamp>(h, x, y, stamp);
        public static void add_weighted_point(heatmap_t h, uint x, uint y, float w) => InvokeFunction<heatmap_add_weighted_point>(h, x, y, w);
        public static void add_weighted_point_with_stamp(heatmap_t h, uint x, uint y, float w, heatmap_stamp_t stamp)
            => InvokeFunction<heatmap_add_weighted_point_with_stamp>(h, x, y, w, stamp);

        public static void render_default_to(heatmap_t h, IntPtr colorbuf) => InvokeFunction<heatmap_render_default_to>(h, colorbuf);

        public static void png_gen(heatmap_t h, string path) => InvokeFunction<heatmap_png_gen>(h, path);

        public static void png_gen2(IntPtr buf, uint w, uint h, string path) => InvokeFunction<heatmap_png_gen2>(buf, w, h, path);

        public static heatmap_stamp_t stamp_gen(uint radius) => InvokeFunction<heatmap_stamp_t, heatmap_stamp_gen>(radius);
        public static void stamp_free(heatmap_stamp_t s) => InvokeFunction<heatmap_stamp_free>(s);




        //dll接口定义
        private delegate heatmap_stamp_t heatmap_stamp_gen(uint radius);
        private delegate void heatmap_stamp_free(heatmap_stamp_t s);

        private delegate heatmap_t heatmap_new(uint w, uint h);
        private delegate void heatmap_free(heatmap_t h);

      

        private delegate void heatmap_add_point(heatmap_t h, uint x, uint y);
        private delegate void heatmap_add_point_with_stamp(heatmap_t h, uint x, uint y, heatmap_stamp_t stamp);

        private delegate void heatmap_add_weighted_point(heatmap_t h, uint x, uint y, float w);        
        private delegate void heatmap_add_weighted_point_with_stamp(heatmap_t h, uint x, uint y, float w, heatmap_stamp_t stamp);


        private delegate IntPtr heatmap_render_default_to(heatmap_t h, IntPtr colorbuf);

        private delegate void heatmap_png_gen(heatmap_t h, string path);

        private delegate void heatmap_png_gen2(IntPtr buf, uint w, uint h, string path);
    }

    public class RgbaBitmapSource : BitmapSource
    {
        private byte[] rgbaBuffer;
        private int pixelWidth;
        private int pixelHeight;

        public RgbaBitmapSource(byte[] rgbaBuffer, int pixelWidth)
        {
            this.rgbaBuffer = rgbaBuffer;
            this.pixelWidth = pixelWidth;
            this.pixelHeight = rgbaBuffer.Length / (4 * pixelWidth);
        }

        unsafe public override void CopyPixels(Int32Rect sourceRect, Array pixels, int stride, int offset)
        {
            fixed (byte* source = rgbaBuffer, destination = (byte[])pixels)
            {
                byte* dstPtr = destination + offset;

                for (int y = sourceRect.Y; y < sourceRect.Y + sourceRect.Height; y++)
                {
                    for (int x = sourceRect.X; x < sourceRect.X + sourceRect.Width; x++)
                    {
                        byte* srcPtr = source + stride * y + 4 * x;
                        byte a = *(srcPtr + 3);
                        *(dstPtr++) = (byte)(*(srcPtr + 2) * a / 256); // pre-multiplied B
                        *(dstPtr++) = (byte)(*(srcPtr + 1) * a / 256); // pre-multiplied G
                        *(dstPtr++) = (byte)(*srcPtr * a / 256); // pre-multiplied R
                        *(dstPtr++) = a;
                    }
                }
            }
        }

        //public override void CopyPixels(
        //    Int32Rect sourceRect, Array pixels, int stride, int offset)
        //{
        //    for (int y = sourceRect.Y; y < sourceRect.Y + sourceRect.Height; y++)
        //    {
        //        for (int x = sourceRect.X; x < sourceRect.X + sourceRect.Width; x++)
        //        {
        //            int i = stride * y + 4 * x;
        //            byte a = rgbaBuffer[i + 3];
        //            byte r = (byte)(rgbaBuffer[i] * a / 256); // pre-multiplied R
        //            byte g = (byte)(rgbaBuffer[i + 1] * a / 256); // pre-multiplied G
        //            byte b = (byte)(rgbaBuffer[i + 2] * a / 256); // pre-multiplied B

        //            pixels.SetValue(b, i + offset);
        //            pixels.SetValue(g, i + offset + 1);
        //            pixels.SetValue(r, i + offset + 2);
        //            pixels.SetValue(a, i + offset + 3);
        //        }
        //    }
        //}

        protected override Freezable CreateInstanceCore()
        {
            return new RgbaBitmapSource(rgbaBuffer, pixelWidth);
        }

        public override event EventHandler<DownloadProgressEventArgs> DownloadProgress;
        public override event EventHandler DownloadCompleted;
        public override event EventHandler<ExceptionEventArgs> DownloadFailed;
        public override event EventHandler<ExceptionEventArgs> DecodeFailed;

        public override double DpiX
        {
            get { return 96; }
        }

        public override double DpiY
        {
            get { return 96; }
        }

        public override PixelFormat Format
        {
            get { return PixelFormats.Pbgra32; }
        }

        public override int PixelWidth
        {
            get { return pixelWidth; }
        }

        public override int PixelHeight
        {
            get { return pixelHeight; }
        }

        public override double Width
        {
            get { return pixelWidth; }
        }

        public override double Height
        {
            get { return pixelHeight; }
        }
    }
}
